From e21bb08f59053150773a48d8abac0102bf091ccb Mon Sep 17 00:00:00 2001 From: James Bendig Date: Mon, 20 Feb 2017 13:31:03 -0600 Subject: [PATCH] Fixed required-features not working with dependencies' features for issue #3727. --- src/cargo/ops/cargo_compile.rs | 47 +++++-- tests/required-features.rs | 234 +++++++++++++++++++++++++++++++++ 2 files changed, 268 insertions(+), 13 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index ee882b7b6..38c0efa00 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -28,7 +28,8 @@ use std::path::PathBuf; use std::sync::Arc; use core::{Source, Package, Target}; -use core::{Profile, TargetKind, Profiles, Workspace, PackageIdSpec}; +use core::{Profile, TargetKind, Profiles, Workspace, PackageId, PackageIdSpec}; +use core::resolver::Resolve; use ops::{self, BuildOutput, Executor, DefaultExecutor}; use util::config::Config; use util::{CargoResult, profile}; @@ -188,8 +189,8 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } } else { let root_package = ws.current()?; - let all_features = resolve_with_overrides.features(root_package.package_id()); - generate_targets(root_package, profiles, mode, filter, all_features, release)?; + let all_features = resolve_all_features(&resolve_with_overrides, root_package.package_id()); + generate_targets(root_package, profiles, mode, filter, &all_features, release)?; pkgids.push(root_package.package_id()); }; @@ -206,9 +207,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, panic!("`rustc` and `rustdoc` should not accept multiple `-p` flags") } (Some(args), _) => { - let all_features = resolve_with_overrides.features(to_builds[0].package_id()); + let all_features = resolve_all_features(&resolve_with_overrides, to_builds[0].package_id()); let targets = generate_targets(to_builds[0], profiles, - mode, filter, all_features, release)?; + mode, filter, &all_features, release)?; if targets.len() == 1 { let (target, profile) = targets[0]; let mut profile = profile.clone(); @@ -221,9 +222,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } } (None, Some(args)) => { - let all_features = resolve_with_overrides.features(to_builds[0].package_id()); + let all_features = resolve_all_features(&resolve_with_overrides, to_builds[0].package_id()); let targets = generate_targets(to_builds[0], profiles, - mode, filter, all_features, release)?; + mode, filter, &all_features, release)?; if targets.len() == 1 { let (target, profile) = targets[0]; let mut profile = profile.clone(); @@ -237,9 +238,9 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, } (None, None) => { for &to_build in to_builds.iter() { - let all_features = resolve_with_overrides.features(to_build.package_id()); + let all_features = resolve_all_features(&resolve_with_overrides, to_build.package_id()); let targets = generate_targets(to_build, profiles, mode, - filter, all_features, release)?; + filter, &all_features, release)?; package_targets.push((to_build, targets)); } } @@ -273,7 +274,29 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>, ret.to_doc_test = to_builds.iter().map(|&p| p.clone()).collect(); - Ok(ret) + return Ok(ret); + + fn resolve_all_features(resolve_with_overrides: &Resolve, + package_id: &PackageId) + -> HashSet { + let mut features = match resolve_with_overrides.features(package_id) { + Some(all_features) => all_features.clone(), + None => HashSet::new(), + }; + + // Include features enabled for use by dependencies so targets can also use them with the + // required-features field when deciding whether to be built or skipped. + let deps = resolve_with_overrides.deps(package_id); + for dep in deps { + if let Some(dep_features) = resolve_with_overrides.features(dep) { + for feature in dep_features { + features.insert(dep.name().to_string() + "/" + feature); + } + } + } + + features + } } impl<'a> CompileFilter<'a> { @@ -318,7 +341,7 @@ fn generate_targets<'a>(pkg: &'a Package, profiles: &'a Profiles, mode: CompileMode, filter: &CompileFilter, - features: Option<&HashSet>, + features: &HashSet, release: bool) -> CargoResult> { let build = if release {&profiles.release} else {&profiles.dev}; @@ -430,8 +453,6 @@ fn generate_targets<'a>(pkg: &'a Package, }; //Collect the targets that are libraries or have all required features available. - let no_features = HashSet::new(); - let features = features.unwrap_or(&no_features); let mut compatible_targets = Vec::with_capacity(targets.len()); for (target, profile) in targets.drain(0..) { if target.is_lib() || match target.required_features() { diff --git a/tests/required-features.rs b/tests/required-features.rs index c230ddb19..e65ea4c16 100644 --- a/tests/required-features.rs +++ b/tests/required-features.rs @@ -745,3 +745,237 @@ fn install_multiple_required_features() { assert_that(cargo_home(), not(has_installed_exe("foo_1"))); assert_that(cargo_home(), not(has_installed_exe("foo_2"))); } + +#[test] +fn dep_feature_in_toml() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar", features = ["a"] } + + [[bin]] + name = "foo" + required-features = ["bar/a"] + + [[example]] + name = "foo" + required-features = ["bar/a"] + + [[test]] + name = "foo" + required-features = ["bar/a"] + + [[bench]] + name = "foo" + required-features = ["bar/a"] + "#) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .file("tests/foo.rs", "#[test]\nfn test() {}") + .file("benches/foo.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#) + .file("bar/src/lib.rs", ""); + p.build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + + // bin + assert_that(p.cargo("build").arg("--bin=foo"), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + // example + assert_that(p.cargo("build").arg("--example=foo"), + execs().with_status(0)); + assert_that(&p.bin("examples/foo"), existing_file()); + + // test + assert_that(p.cargo("test").arg("--test=foo"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + // bench + if is_nightly() { + assert_that(p.cargo("bench").arg("--bench=foo"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] bar v0.0.1 ({0}/bar) +[COMPILING] foo v0.0.1 ({0}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + } + + // install + assert_that(p.cargo("install"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo("uninstall").arg("foo"), + execs().with_status(0)); +} + +#[test] +fn dep_feature_in_cmd_line() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + + [[bin]] + name = "foo" + required-features = ["bar/a"] + + [[example]] + name = "foo" + required-features = ["bar/a"] + + [[test]] + name = "foo" + required-features = ["bar/a"] + + [[bench]] + name = "foo" + required-features = ["bar/a"] + "#) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .file("tests/foo.rs", "#[test]\nfn test() {}") + .file("benches/foo.rs", r#" + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_: &mut test::Bencher) { + }"#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#) + .file("bar/src/lib.rs", ""); + p.build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + + // bin + assert_that(p.cargo("build").arg("--bin=foo"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `bar/a` +Consider enabling them by passing e.g. `--features=\"bar/a\"` +")); + + assert_that(p.cargo("build").arg("--bin=foo").arg("--features").arg("bar/a"), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + // example + assert_that(p.cargo("build").arg("--example=foo"), + execs().with_status(101).with_stderr("\ +error: target `foo` requires the features: `bar/a` +Consider enabling them by passing e.g. `--features=\"bar/a\"` +")); + + assert_that(p.cargo("build").arg("--example=foo").arg("--features").arg("bar/a"), + execs().with_status(0)); + assert_that(&p.bin("examples/foo"), existing_file()); + + // test + assert_that(p.cargo("test"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")) + .with_stdout("")); + + assert_that(p.cargo("test").arg("--test=foo").arg("--features").arg("bar/a"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] target[/]debug[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); + + // bench + if is_nightly() { + assert_that(p.cargo("bench"), + execs().with_status(0).with_stderr(format!("\ +[FINISHED] release [optimized] target(s) in [..]")) + .with_stdout("")); + + assert_that(p.cargo("bench").arg("--bench=foo").arg("--features").arg("bar/a"), + execs().with_status(0).with_stderr(format!("\ +[COMPILING] bar v0.0.1 ({0}/bar) +[COMPILING] foo v0.0.1 ({0}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE]", p.url())) + .with_stdout(" +running 1 test +test bench ... bench: [..] 0 ns/iter (+/- 0) + +test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured + +")); + } + + // install + assert_that(p.cargo("install"), + execs().with_status(101).with_stderr(format!("\ +[INSTALLING] foo v0.0.1 ([..]) +[FINISHED] release [optimized] target(s) in [..] +[ERROR] no binaries are available for install using the selected features +"))); + assert_that(cargo_home(), not(has_installed_exe("foo"))); + + assert_that(p.cargo("install").arg("--features").arg("bar/a"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(p.cargo("uninstall").arg("foo"), + execs().with_status(0)); +} -- 2.30.2